package com.lwl.mini.executor.parameter;

import com.lwl.mini.binding.BindingException;
import com.lwl.mini.mapping.MapperStatement;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;

/**
 * 处理sql语句
 *
 * @author lwl
 * @create 2022/4/18 13:02
 */
public class DefaultParameterHandler implements ParameterHandler {
    /**
     * SQL信息
     */
    MapperStatement statement;

    /**
     * 有序map处理顺序,顺序靠前的优先处理
     * key存在sql中子字符串的首坐标，value存字符串
     */
    Map<Integer, String> processing = new TreeMap<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1-o2;
        }
    });

    public DefaultParameterHandler(MapperStatement statement) {
        this.statement = statement;
    }

    @Override
    public PreparedStatement prepare(Connection connection) {
        String sql = evalSql();
        Map<Object, Object> paramMap = statement.getParamMap();
        PreparedStatement statement = null;
        try {
            int index = 1;
            statement = connection.prepareStatement(sql);
            for (Integer key : processing.keySet()) {
                Object obj = paramMap.get(processing.get(key));
                statement.setObject(index++,obj);
            }
        } catch (SQLException e) {
            throw new BindingException("预处理sql语句异常:"+e,e);
        }
        return statement;
    }

    /**
     * 替换#{..} 为 ?
     *
     * @param sql
     * @return
     */
    public String evalSql() {
        //kmp算法解决字符串匹配
        String sql = statement.getStatement();

        //存坐标
        Set<Integer> set = new HashSet<>();
        for (Object key : statement.getParamMap().keySet()) {
            String temp = "#{" + key + "}";
            int indexOf = getIndexOf(sql, temp);
            if (indexOf == -1) {
                throw new BindingException("未在SQL中找到匹配字符串:\nSQL:" + sql + "\nMatch:" + temp);
            }
            processing.put(indexOf, (String) key);
            sql = sql.substring(0, indexOf) + "?" + sql.substring(indexOf + temp.length(), sql.length());
        }
        return sql;
    }

    /**
     * KMP算法，获得子字符串的下标
     * 时间复杂度:O(N)
     */
    public int getIndexOf(String s, String m) {
        if (s == null || s.length() < 1 || m == null || m.length() < 1) {
            return -1;
        }
        char[] str = s.toCharArray();
        char[] match = m.toCharArray();
        //str当前比对位置
        int x = 0;
        //match当前比对位置
        int y = 0;
        int[] next = getNextArray(match);

        while (x < str.length && y < match.length) {
            if (str[x] == match[y]) {
                x++;
                y++;
            } else if (y == 0) {
                //换个开头
                x++;
            } else {
                y = next[y];
            }
        }
        return y == match.length ? x - y : -1;
    }


    /**
     * 获得字符前缀和后缀相等的数量最大有多少
     * 时间复杂度为O(M) M<=N
     */
    public int[] getNextArray(char[] match) {
        if (match.length == 1) {
            return new int[]{-1};
        }
        int[] next = new int[match.length];
        next[0] = -1;
        next[1] = 0;
        //下标从2开始
        int i = 2;
        int cn = 0;
        while (i < next.length) {
            if (match[i - 1] == match[cn]) {
                next[i++] = ++cn;
            } else if (cn > 0) {
                cn = next[cn];
            } else {
                next[i++] = 0;
            }
        }
        return next;
    }
}
